package cate.game.pvp.eliterace.handler;

import cate.common.table.d.GD;
import cate.common.table.d.GDMail;
import cate.common.table.d.GDObj;
import cate.game.GameBody;
import cate.game.event.eliterace.EliteRaceTimerEvent;
import cate.game.framework.ThreadShare;
import cate.game.mail.po.MailBuilder;
import cate.game.play.part.PlaySide;
import cate.game.pvp.eliterace.EliteRaceDataManager;
import cate.game.pvp.eliterace.data.*;
import cate.game.pvp.eliterace.handler.EliteRaceRankRwdOperation.QueryParam;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.Role;
import cate.game.role.fight.FuncPosition;
import cate.game.role.fight.Position;
import cate.game.role.mail.Mail;
import cate.game.role.pvp.eliterace.msg.EliteRaceBetReq;
import cate.game.role.pvp.eliterace.msg.EliteRaceSpecResp;
import com.google.common.collect.Lists;
import cp.solution.business.game.component.NoticeParam;
import easy.java.practice.errorcode.EcResult;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static cate.common.table.d.GDFunc.ELITE_RACE;
import static cate.game.client.msg.MsgSender.send;
import static cate.game.pvp.eliterace.util.EliteRaceSpecUtils.isReadyStep;

@Slf4j
public abstract class EliteRaceHandler
        implements EliteRaceRankHandler,
        EliteRaceMessageHandler,
        EliteRaceTimerEvent.Handler {

    @Getter
    private final GameBody gameBody;

    @Getter
    private final EliteRaceDataManager dataManager;

    public EliteRaceHandler(GameBody gameBody) {
        this.gameBody = Objects.requireNonNull(gameBody);
        this.dataManager = Objects.requireNonNull(gameBody.pvp.eliteRace.getDataManager());
    }

    public static LocalEliteRaceHandler ofLocal(GameBody gameBody) {
        return new LocalEliteRaceHandler(gameBody);
    }

    public long getSeasonStartTime() {
        return dataManager.getData().getOpenTime();
    }

    public long getNextSeasonStartTime() {
        EliteRaceKV kv = getDataManager().getData();
        return kv.nextStartTime();
    }

    public int getSeason() {
        return dataManager.getData().getSeason();
    }

    public abstract void updatePosition(Role role, FuncPosition funcPosition);

    protected void handleSyncPosition(String uid) {
        Role role = getGameBody().role.getRole(uid);
        if (Objects.isNull(role)) {
            return;
        }

        if (getDataManager().getData().getSeason() == 1 && (this instanceof LocalEliteRaceHandler)) {
            LocalEliteRaceHandler localHandler = (LocalEliteRaceHandler) this;
            localHandler.handleSyncLocalPosition(role);
        }

        Position position = role.getFight().getPosition(ELITE_RACE, 1);
        if (Objects.isNull(position)) {
            return;
        }

        FuncPosition funcPosition = new FuncPosition();
        funcPosition.funcId = ELITE_RACE;
        funcPosition.positions.add(position.copy());
        funcPosition.init();

        updatePosition(role, funcPosition);
    }

    public EcResult<Void> handleBetReq(Role role, EliteRaceBetReq req) {
        return role.getPvp().eliteRace.betPO.checkBet(req);
    }

    protected void noticeUpdateSpec(Role role, EliteRaceSpec spec) {
        send(role::sendNow, new EliteRaceSpecResp(spec));
    }

    protected void queryInvitedOpponentsAndHandle() {
        EcResult<List<EliteRaceOpponent>> r = handleQueryPs(getGameBody().config.srvId);
        if (!r.ok() || CollectionUtils.isEmpty(r.data)) {
            return;
        }

        List<EliteRaceOpponent> queryList = r.data;
        queryList.forEach(this::sendInvitation);
    }

    private void sendInvitation(EliteRaceOpponent opponent) {
        Role role = getGameBody().role.getRole(opponent.getSnapshot().base.uid);
        if (Objects.nonNull(role) && role.getStatus().isOnline()) {
            ThreadShare.instance().exe(role.getCc(),
                    () -> role.getPvp().eliteRace.handleInvitation(opponent.getPrRank()));
        }
    }

    protected void queryRankRwdAndHandle() {
        EliteRaceKV kv = getDataManager().getData();
        QueryParam param = QueryParam.of(kv.getSeason(), getGameBody().config.srvId);
        EcResult<List<EliteRaceRankRwd>> r = handleQueryRankRwd(param);
        if (!r.ok() || CollectionUtils.isEmpty(r.data)) {
            log.error("发送精英赛排行榜奖励为空!");
            return;
        }

        for (EliteRaceRankRwd rwd : r.data) {
            log.info("发送第{}赛季精英赛排行榜奖励, 玩家uid：{}，名次：{}", rwd.getSeason(), rwd.getUid(), rwd.getRank());
            int starMax = getGameBody().pvp.status.uidToStarMax.getOrDefault(rwd.getUid(), 0);
            int extra = getGameBody().table.pvpRankPointExtra.base.getExtraParam(starMax);
            Mail mail;
            if (extra > 0) {
                MixRes res = new MixRes(rwd.getReward());
                long count = res.count(GDObj.Type.NUM, GDObj.Num.RACE_POINT);
                long addNum = count * extra / GD.W_10000;
                MixResItem item = new MixResItem(GDObj.Type.NUM, GDObj.Num.RACE_POINT, addNum);
                res.addItem(item);
                mail = new MailBuilder()
                        .setContentId(GDMail.Tid.ELITE_RACE_RANK_REWARD_EX)
                        .setNoticeParamList(Lists.newArrayList(
                                new NoticeParam(rwd.getRank()),
                                new NoticeParam(starMax),
                                new NoticeParam((extra / 100.0) + "%"),
                                new NoticeParam(NoticeParam.NoticeType.MIX_RES, new MixRes().addItem(item))))
                        .setReward(res)
                        .build();
            } else {
                mail = new MailBuilder()
                        .setContentId(GDMail.Tid.ELITE_RACE_RANK_REWARD)
                        .setParamList(Lists.newArrayList(rwd.getRank()))
                        .setReward(new MixRes(rwd.getReward()))
                        .build();
            }

            getGameBody().mail.send(rwd.getUid(), mail);

            Role role = getGameBody().role.getRole(rwd.getUid());
            if (Objects.nonNull(role)) {
                role.getPvp().eliteRace.refreshRecord(rwd.getRank());
            }
        }
    }

    protected void updatePositionAndHandle(EliteRaceSpec spec) {
        if (!isReadyStep(spec)) {
            return;
        }

        EcResult<Map<String, String>> r = handleQueryPlayers(gameBody.config.srvId, spec);
        if (!r.ok() || CollectionUtils.isEmpty(r.data)) {
            return;
        }

        Map<String, String> queryMap = r.data;

        List<EliteRacePs> psList = queryMap.entrySet().stream()
                .map(this::updatePosition)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(psList)) {
            psList.forEach(eps -> handleUpdatePs(spec, eps));
        }
    }

    private EliteRacePs updatePosition(Map.Entry<String, String> entry) {
        String uid = entry.getKey();
        String md5 = entry.getValue();

        Role role = gameBody.role.getRole(uid);
        if (Objects.isNull(role)) {
            return null;
        }

        EcResult<PlaySide> r = getGameBody().fight.side.createPlaySideByRole(role, ELITE_RACE, 1);
        if (!r.ok() || Objects.isNull(r.data)) {
            return null;
        }

        PlaySide ps = r.data;

        EcResult<EliteRacePs> psr = EliteRacePs.compressEc(ps, 0L);
        if (!psr.ok() || Objects.isNull(psr.data)) {
            return null;
        }

        EliteRacePs eps = psr.data;

        if (StringUtils.endsWithIgnoreCase(eps.getCpsList().getLast().getMd5Sum(), md5)) {
            return null;
        }

        return eps;
    }

}
